#include <iostream>
#include <queue>
#include <pthread.h>

size_t def = 5; //默认商品数量

template<class T>
class BlockQueue
{
public:
    BlockQueue(size_t cap = def)
        :_cap(cap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_full,nullptr);
        pthread_cond_init(&_empty,nullptr);
    }

    void push(const T& data)
    {
        pthread_mutex_lock(&_mutex); //要访问临界资源必须先申请锁
        while(isfull()) //判断队是否为满 使用while防止调度错误问题同时释放太多线程导致异常
        {
            pthread_cond_wait(&_full,&_mutex); //进入队满挂起
        }
        //出来则表示可生产
        _bq.push(data);
        //消费调度策略 1.每次生产都进行调度 2.判断产品量调度
        
        if(_bq.size() > _cap/2) //商品数量比较多
            pthread_cond_broadcast(&_empty); //默认策略一个一个唤醒
        else
            pthread_cond_signal(&_empty); //默认策略一个一个唤醒

        pthread_mutex_unlock(&_mutex); //做完后释放锁
    }

    void pop(T& data)
    {
        pthread_mutex_lock(&_mutex);
        while(isempty()) //判断队是否为空
        {
            pthread_cond_wait(&_empty,&_mutex); //进入队空挂起
        }
        data = _bq.front();
        _bq.pop();
        //生产调度策略 1.每次消费都进行调度 2.判断产品量调度
        if(_bq.size() < _cap/2) //商品数量比较少
            pthread_cond_broadcast(&_full); //默认策略一个一个唤醒
        else
            pthread_cond_signal(&_full); //默认策略一个一个唤醒

        pthread_mutex_unlock(&_mutex);
    }

    bool isfull() {return _cap == _bq.size();} //判断释放队满 满为真
    bool isempty() {return _bq.empty();} //判断释放队空 空为真

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_full);
        pthread_cond_destroy(&_empty);
    }

private:
    std::queue<T> _bq; //商品队列
    size_t _cap; //商品最大数量
    pthread_mutex_t _mutex; //互斥锁 在线程访问时只允许一个线程访问队列

    //条件变量 控制同步 当商品生产满时停止 消费完时停止消费 互相通知对方
    pthread_cond_t _full; //生产者商品满队列
    pthread_cond_t _empty; //消费者商品空队列

};